# coding: utf-8

import os
import re
import time
import ujson
import tornado
import functools
import subprocess
from datetime import datetime
from tornado.util import ObjectDict
from ci_conclude.log import logger, logThrown
from ci_conclude.utils.error_code import ERROR
from ci_conclude.utils.error import BaseException, FailureException
from voluptuous import MultipleInvalid, Schema, ALLOW_EXTRA, REMOVE_EXTRA, Required, All, Any, Coerce, Length
import threading


# class FailureException(Exception):

#     def __init__(self, error_code, error_message, data):
#         super().__init__(f"{error_code}({error_message})", data)
#         self.error_code = error_code
#         self.error_message = error_message
#         self.data = data
#         # from code import interact
#         # interact(local=locals())
    
#     def get_return_object(self):
#         return {
#                 'code': self.error_code,
#                 'message': self.error_message,
#                 'data': self.data
#             }

#     def __unicode__(self):
#         return ujson.dumps(self.get_return_object())

#     def __str__(self):
#         return self.__unicode__()
    
#     def __repr__(self):
#         return self.__unicode__()

class BaseService(object):
    def __init__(self, handler, params=None):
        self.handler = handler
        self.params = {}
        if hasattr(self.handler, "db"):
            self.db = handler.db
        if self.handler and hasattr(self.handler, "args"):
            self.params.update(self.handler.args)
        else:
            setattr(self.handler, "args", {})
        if self.handler and hasattr(self.handler, 'request'):
            names = self.handler.request.arguments.keys()
            self.params.update({k: self.handler.get_argument(k) for k in names})
        if self.handler and hasattr(self.handler, "param_schema"):
            self.params.update(self.handler.param_schema)
        if params:
            self.params.update(params)
        else:
            self.params.update({})
        if self.handler and hasattr(self.handler, "request"):
            multiple_body_kwargs = {k:v for k, v in self.handler.request.body_arguments.items() if len(v) > 1}
            self.params.update(multiple_body_kwargs)
            multiple_query_kwargs = {k:v for k, v in self.handler.request.query_arguments.items() if len(v) > 1}
            self.params.update(multiple_query_kwargs)
            multiple_files_kwargs = {k:v for k, v in self.handler.request.files.items() if len(v) > 0}
            self.params.update(multiple_files_kwargs)
            if self.handler.request.body and "application/json" in self.handler.request.headers.get("Content-Type"):
                try:
                    b = self.handler.request.body
                    # b = eval(repr(b).replace("\\\\", "\\"))
                    json_params = ujson.loads(b)
                    self.params.update(json_params)
                except ValueError as e:
                    logThrown()
                    logger.error(f"self.handler.request.body: {self.handler.request.body}")
                # from code import interact
                # interact(local=locals())

        if self.handler and hasattr(self.handler, "method_kwargs"):
            self.params.update(self.handler.method_kwargs)
        # logger.info(self.params)

    def success(self, data=None, **kwargs):
        result = ObjectDict()
        result.code = 0
        result.message = u""
        result.data = data
        for k, v in kwargs.items():
                result[k] = v
        return result

    def failure(self, error_obj, data=None):
        return FailureException(error_obj, data).get_return_object()
        # result = ObjectDict()
        # result.code = error_obj.errcode
        # result.message = error_obj.errvalue
        # result.data = data
        # return result

    def failureMessage(self, error_message):
        return BaseException(500, error_message, data=None)
        # result = ObjectDict()
        # result.code = 500
        # result.message = error_message
        # result.data = None
        # return result

    def check_schema(self, schema):
        try:
            check_result = schema(self.params)
            return self.success(data=ObjectDict(check_result))
        except MultipleInvalid as e:
            logger.info("#"*100)
            logger.info("# %s" % self)
            logger.info("# validate params")
            logger.info("# %s" % self.params)
            logger.info("# schema")
            logger.info(schema)
            logger.info("#"*100)
            for err in e.errors:
                logger.error("validate error: %s: %s" % (err.path[0], err.msg))
            error_data = {e.path[0]: e.msg for e in e.errors}
            # return self.failure(ERROR.params_validate_err, data=error_data)
            raise FailureException(ERROR.params_validate_err, data=error_data)
        except AssertionError as e:
            logThrown()
            raise FailureException(ERROR.params_validate_err, data=e.__str__())
        except Exception as e:
            logThrown()
            return self.failure(ERROR.params_validate_err, data=e.__unicode__())
    
    def getPage(self, object_list, page, pageSize, total):
        pageStart = (page - 1) * pageSize
        total = len(object_list)
        pageEnd = page * pageSize
        pageEnd = pageEnd if pageEnd < total else total
        object_items = object_list[pageStart:pageEnd]
        pageCount = int((total + pageSize - 1) / pageSize)
        pageStart += 1
        if pageStart >= pageEnd:
            pageStart = pageEnd
        paginate = dict(
                pageNum=page,
                pageSize=pageSize,
                total=total,
                pageCount=pageCount,
                pageStart=pageStart,
                pageEnd=pageEnd,
            )
        return object_items, paginate


def timeit(level="info", only=None):
    def func(method):
        def wrapper(self, *args, **kwargs):
            logger_func = {
                "warning": logger.warning,
                "error": logger.error,
                "debug": logger.debug,
                "critical": logger.critical,
                "info": logger.info,
            }.get(level)
            setattr(self, "logger_func", logger_func)
            logger_func("*"*60)
            logger_func("[%s: %s]" % (self.__class__.__name__, method.__name__))
            logger_func("*"*60)
            t1 = time.time()
            result = method(self, *args, **kwargs)
            t2 = time.time()
            delta_t = int(t2*1000) - int(t1*1000)
            logger_func("%s.%s: t2 - t1 = %s" % (self.__class__.__name__, method.__name__, delta_t))
            return result
        return wrapper
    return func
